
#ifndef PROTOTYPES_H
#define PROTOTYPES_H

string Symbol::compile(Token* super)
{
	return super->value;
}

string Symbol::compileDef(Token* super)
{
	return "";
}

struct Infix : public Symbol
{
	Type type(Token* n)
	{
		Type mix = (Type)(n->first->type() | n->second->type());
		if (mix > Double)
			n->error(" is not valid for these type of operands.");
		return mix;
	}

	Token* led(Token* super, Token* left) 
	{
		super->setFirst(left);
		super->setSecond(expression(lbp));
		super->arity = Token::Binary;
		return super;
	}

	string compile(Token* super)
	{
		string code = super->first->compile();
		code += " " + super->id() + " ";
		code += super->second->compile();
		return code;
	}
};

struct StrictInfix : public Infix
{
	Type m_Type, m_Strict;
	StrictInfix(Type type, Type strict)
	{
		m_Type = type; m_Strict = strict;
	}

	Type type(Token* super)
	{
		Type mix = (Type)(super->first->type() | super->second->type());
		if (mix > m_Strict)
			super->error(" is illegal for these operands.");
		return m_Type;
	}
};

struct Tilda : public Infix
{
	Type type(Token* n)
	{
		if (n->arity != Token::Unary)
			return Var;
		else
			return Integer;
	}

	Token* nud(Token* super) 
	{
		super->setFirst(expression(70));
		super->arity = Token::Unary;
		return super;
	}

	string compile(Token* super)
	{
		string code = "_cat(";
		code += super->first->compile();	
		code += ",";
		code += super->second->compile();
		code += ")";
		return code;
	}
};

struct StrictDoubleInfix: public StrictInfix
{
	string m_Func;

	StrictDoubleInfix(const char* f) : StrictInfix(Double, Double)
	{
		m_Func = f;
	}

	string compile(Token* super)
	{
		string code = m_Func;
		code += "(";
		code += super->first->compile();	
		code += ",";
		code += super->second->compile();
		code += ")";
		return code;
	}
};

struct Minus : public StrictInfix
{
	Minus() : StrictInfix(Integer, Double)
	{
	}

	Type type(Token* n)
	{
		if (n->arity == Token::Unary)
		{
			return n->first->type();
		}
		else return StrictInfix::type(n);
	}

	Token* nud(Token* super) 
	{
		super->setFirst(expression(70));
		super->arity = Token::Unary;
		return super;
	}

	string compile(Token* super)
	{
		
		if (super->arity == Token::Binary)
		{
			return Infix::compile(super);
		}
		else
		{
			string code = super->id();
			code += super->first->compile();
			return code;
		}
		return "";
	}
};


struct Infixr : public Symbol
{
	Type type(Token*)
	{
		return Symbol::Integer;
	}

	Token* led(Token* super, Token* left) 
	{
		super->setFirst(left);
		super->setSecond(expression(lbp - 1));
		super->arity = Token::Binary;
		return super;
	}

	string compile(Token* super)
	{
		string code = super->first->compile();
		code += " " + super->id() + " ";
		code += super->second->compile();
		return code;
	}
};

struct TripleEqual : public Infixr
{
	string compile(Token* super)
	{
		string code = "_str_eq(";
		code += super->first->compile();
		code += ", ";
		code += super->second->compile();
		code += ")";
		return code;
	}
};


struct TripleNotEqual : public Infixr
{
	string compile(Token* super)
	{
		string code = "_str_neq(";
		code += super->first->compile();
		code += ", ";
		code += super->second->compile();
		code += ")";
		return code;
	}
};

struct Question : public Symbol
{
	Type type(Token* n)
	{
		return (Symbol::Type)(n->second->type() | n->third->type());
	}

	Token* led(Token* super, Token* left) 
	{
		super->setFirst(left);
		super->setSecond(expression(9));
		advance(":");
		super->setThird( expression(9));
		super->arity = Token::Ternary;
		return super;
	}

	string compile(Token* super)
	{
		string code = super->first->compile();
		code +=	" ? ";
		code += super->second->compile();
		code += " : ";
		code += super->third->compile();
		return code;
	}
};



struct Dot : public Symbol
{
	Type type(Token* n)
	{
		return Var;
	}

	Token* led(Token* super, Token* left) 
	{
		super->setFirst(left);
		if (token->arity != Token::Name) {
			token->error(Error::ExpectedPropertyName);
		}
		
		super->arity = Token::Binary;
//		token->arity = "literal";
		super->setSecond(token);

		advance();
	
		return super;
	}

	string compile(Token* super)
	{
		string code = super->first->compile();
		code += "->";
		code += super->second->compile();
		return code;
	}
};


struct Arrow : public Symbol
{
	Type type(Token* n)
	{
		return Var;
	}

	Token* led(Token* super, Token* left) 
	{
		super->setFirst(left);
		if (token->arity != Token::Name) {
			token->error(Error::ExpectedPropertyName);
		}
		
		super->arity = Token::Binary;
		super->setSecond(token);

		advance();
	
		return super;
	}

	string compile(Token* super)
	{
		string code = "_map_of(";
		code += super->first->compile();
		code += ",\"";
		code += super->second->value;
		code += "\")";
		return code;
	}
};

struct Colon : public Symbol
{
	Token* led(Token* super, Token* left) 
	{
		super->setFirst(left);
		if (token->arity != Token::Name) {
			token->error(Error::ExpectedPropertyName);
		}
		
		super->arity = Token::Binary;
		super->setSecond(token);

		advance();
	
		return super;
	}

	string compile(Token* super)
	{
		string code = "static_cast<";
		code += super->second->value;
		code += "*>(";
		code += super->first->compile();
		code += ".o)";
		return code;
	}
};


struct Square_Bracket : public Symbol
{
	Type type(Token* n)
	{
		return Var;
	}

	Token* led(Token* super, Token* left) 
	{
		super->setFirst(left);
		super->setSecond(expression(9));
		super->arity = Token::Binary;
		advance("]");
		return super;
	}

	Token* nud(Token* super)
	{
		if (token->id() != "]") {
			while (true) {
				super->push(expression(9));
				if (token->id() != ",") {
					break;
				}
				advance(",");
			}
		}
		advance("]");
		super->arity = Token::Unary;
		return super;
	}

	string compile(Token* super)
	{
		if (super->arity == Token::Unary)
		{
			string code = "(new Array())";
			if (super->leaves)
			for (int i = 0; i < super->leaves->size(); i++)
			{
				code += "->push_init(";
				code += super->leaves->at(i)->compile();
				code += ")";
			}
			return code;
		}
		else
		{
			string code = super->first->compile();
			code +=	"[";
			code += super->second->compile();
			code += "]";
			return code;
		}
		return "";
	}
};

struct Prefix : public Symbol
{
	Type type(Token*)
	{
		return Symbol::Integer;
	}

	Token* nud(Token* super) 
	{
		super->setFirst(expression(70));
		super->arity = Token::Unary;
		return super;
	}

	string compile(Token* super)
	{
		string code = super->id();
		code += super->first->compile();
		return code;
	}
};

struct New : public Symbol
{
	Type type(Token*)
	{
		return Ref;
	}

	Token* nud(Token* super)
	{
		Token* a = new Token();

		if (token->arity != Token::Name) 
		{
			token->error(Error::ExpectedClassName);
		}

		super->setFirst(token);
		advance();
		advance("(");
		if (token->id() != ")") 
		{
			while (true)  {
				a->push(expression(9));
				if (token->id() != ",") {
					break;
				}
				advance(",");
			}
		}
		advance(")");
		super->setSecond(a);
		super->arity = Token::Binary;
		return super;
	}

	string compile(Token* super)
	{
		if (super->parent != NULL && super->parent->id() != "=" && super->parent->id() != "(")
		{
			super->error(Error::NotUsedInAssign);
		}

		string code = " (New*)";
		code += super->id();
		code += " ";
		code +=	super->first->compile();
		code += "(";

		if (super->second->leaves->size())
		{
			for (unsigned i = 0; i < super->second->leaves->size() - 1; i++)
			{
				code += super->second->leaves->at(i)->compile();
				code += ", ";
			}
			code += super->second->leaves->at(super->second->leaves->size() - 1)->compile();
		}
		code += ")";

		return code;
	}
};

struct Bracket : public Symbol
{
	Type type(Token* n)
	{
		if (n->arity == Token::Unary)
			return n->first->type();
		else
			return Var;
	}

	Token* nud(Token* super) 
	{
		super->setFirst(expression(9));
		advance(")");
		super->arity = Token::Unary;
		return super;
	}

	Token* led(Token* super, Token* left)
	{
		Token* a = new Token();
		if (left->id() == "." || left->id() == "[") {
			super->arity = Token::Ternary;
			super->setFirst(left->first);
			super->setSecond(left->second);
			super->setThird(a);
		} else {
			super->arity = Token::Binary;
			super->setFirst(left);
			super->setSecond(a);
			if ((left->arity != Token::Unary || left->id() != "function") &&
					left->arity != Token::Name && left->id() != "(" && 
					left->id() != "&&" && left->id() != "||" && left->id() != "?") 
			{
				left->error(Error::ExpectedVarName);
			}
		}
		if (token->id() != ")") {
			while (true)  {
				a->push(expression(9));
				if (token->id() != ",") {
					break;
				}
				advance(",");
			}
		}
	
		advance(")");
		return super;
	}

	string compile(Token* super)
	{
		if (super->arity == Token::Binary)
		{
			string code;
			code += super->first->compile();
			code += "(";

			if (super->second->leaves->size())
			{
				for (unsigned i = 0; i < super->second->leaves->size() - 1; i++)
				{
					code += super->second->leaves->at(i)->compile();
					code += ", ";
				}
				code += super->second->leaves->at(super->second->leaves->size() - 1)->compile();
			}
			code += ")";
			return code;

		}
		else if (super->arity == Token::Ternary)
		{
			string code;
			if (super->first->arity == Token::Super)
			{
				code += at_parent_class;
				code += "::";
			}
			else if (class_scopes[super->first->value])
			{
				super->first->arity = Token::Class;
				code += super->first->value;
				code += "::";
			}
			else
			{
				code += super->first->compile();
				code += "->";
				if (super->first->type() == Ref)
				{
					code += super->first->refType();
					code += "::";
				}
			}

			code += super->second->compile();
			code += "(";
			if (super->third->leaves->size())
			{
				for (unsigned i = 0; i < super->third->leaves->size() - 1; i++)
				{
					code += super->third->leaves->at(i)->compile();
					code += ", ";
				}
				code += super->third->leaves->at(super->third->leaves->size() - 1)->compile();
			}
			code += ")";
			return code;
		}
		else
		{
			return "(" + super->first->compile() + ")"; 
		}
	}
};

struct Assignment : public Symbol
{
	Assignment()
	{
		assignment = true;
	}

	Token* led(Token* super, Token* left) 
	{
		string& id = left->id();
		if (left->arity != Token::Name && id != "." && id != "[" && id != "->")
		{
			left->error(Error::BadLeftOp);
		}

		super->setFirst(left);
		super->setSecond(expression(9));
		VarName* v = (VarName*)super->scope->find(super->first->value);
		if (v)
		{
			if (v->type == Ref && super->second->type() == Var)
			{
				//super->first->setType(super->second);
				v->type = Var;
			}
			else if ((v->type == Integer || (v->type == Var && v->refType.empty()))  && super->second->type() == Double)
			{
				v->type = Double;
			}
			else if (v->type == Var && v->refType.empty() && super->second->type() == Integer)
			{
				v->type = Integer;
			}
		}

		super->arity = Token::Binary;
		return super;
	}
	
	string compile(Token* super)
	{
		string code;
		if (super->first->arity == Token::Name )
		{
			Symbol::Type type2 = super->second->type();
			Symbol::Type type1 = super->first->type();
			Symbol::Type mix = (Symbol::Type)(type2 | type1);
			
			if (type1 != mix && type1 != Symbol::Var || type1 == Symbol::Ref)
			{
				switch (mix)
				{
				case Unknown:
					super->error("cannot convert " + toString(type1,super->first) + " to " + toString(type2,super->second));
					break;
				case Ref:
					{
						string& ref1 = super->scope->getRefType(super->first->value);
						string ref2 = super->second->id() == "new" ? super->second->first->value : super->scope->getRefType(super->second->value);
						if ( ref1 != ref2 )
						{
							super->error("cannot convert " + ref1 + " to " + ref2);
						}
					}
					break;
				case Var:
				case Double:
					if (type1 == Integer)
					{
						super->warning("convertion from " + toString(type2,super->second) + " to integer, possible loss of data.");
					}
					break;
				}
			}
		}

		code += super->first->compile();
		code += " " + super->id() + " ";
		code += super->second->compile();
	
		return code;
	}
};

struct UnaryAssignment : public Symbol
{
	UnaryAssignment()
	{
		assignment = true;
	}

	Token* led(Token* super, Token* left) 
	{
		string& id = left->id();
		if (left->arity != Token::Name && id != "." && id != "[" && id != "->")
		{
			left->error(Error::BadLeftOp);
		}

		super->setFirst(left);
		super->arity = Token::Unary;
		return super;
	}

	string compile(Token* super)
	{
		Symbol::Type type1 = super->first->type();
		string code = super->first->compile();
		if (type1 == Ref || type1 == Unknown)
			super->error("invalid for " + code);
		code += super->id();
		return code;
	}
};

struct TildaAssignment : public Assignment
{
	string compile(Token* super)
	{
		string code = "_cat_a(";
		code += super->first->compile();
		code += ",";
		code += super->second->compile();
		code += ")";
		return code;
	}
};

struct Constant : public Symbol
{
	Type m_type;
	Type type(Token*)
	{
		return m_type;
	}

	const char* value;

	Constant(const char* v, Type t)
	{
		value = v;
		m_type = t;
	}

	Token* nud(Token* super) 
	{
		super->value = value;
		super->arity = Token::Literal;
		return super;
	}
};

struct This : public Symbol
{
	Type type()
	{
		return Var;
	}

	Token* nud(Token* super) 
	{
		super->arity = Token::This;
		return super;
	}

	Token* std(Token* super)
	{
        if (!in_class)
			super->error(Error::ConstructorOutsideClass);

        if (in_function)
			super->error(Error::ConstructorInsideFunc);

        new_scope();
		advance("(");        
		int args = 0;
        Token* a = new Token();
		if (token->id() != ")") 
		{
			while (true) 
			{
				if (token->arity != Token::Name) {
					token->error(Error::ExpectedParamName);
				}
				scope->defineVar(token);
				a->push(token);
				args++;
				advance();
				if (token->id() != ",") {
					break;
				}
				advance(",");
			}
		}

		if (args > 0)
		{
			constructor_scope->defineFunction(at_class, args);
		}
		super->setFirst(a);
		advance(")");

		super->arity = Token::Binary;
		if (token->id() == ":")
		{
			advance(":");
			advance("super");
			advance("(");
	        Token* s = new Token();
			if (token->id() != ")") 
			{
				while (true)  {
					s->push(expression(9));
					if (token->id() != ",") {
						break;
					}
					advance(",");
				}
			}
			super->setThird(s);
			advance(")");
			super->arity = Token::Ternary;
		}

		advance("{");
        in_function = true;
		super->setSecond(statements());
        scope->pop();
		advance("}");
		
        in_function = false;
		
		return super;
	}

	string compile(Token* super)
	{
		if (super->arity != Token::This)
		{
			string code = "public: ";
			code += at_class;
			code += "(";
			if (super->first->leaves)
			for (unsigned i = 0; i < super->first->leaves->size(); i++)
			{
				code += "var ";
				code += super->first->leaves->at(i)->compile();
				if (i < super->first->leaves->size() - 1)
					code += ", ";
			}
			code += ")";
			return code;
		}
		
		return super->value;
	}

	string compileDef(Token* super)
	{
		def_code += at_class;
		def_code += "::";
		def_code += at_class;
		def_code += "(";
		if (super->first->leaves)
		for (unsigned i = 0; i < super->first->leaves->size(); i++)
		{
			def_code += "var ";
			def_code += super->first->leaves->at(i)->compile();
			if (i < super->first->leaves->size() - 1)
				def_code += ", ";
		}
		def_code += ")";
		if (super->third)
		{
			Token* args = super->third;
			FunctionName* o = static_cast<FunctionName*>(constructor_scope->find(at_parent_class.c_str()));
			if (!o->args[args->leaves->size()])
			{
				super->error(Error::SuperDiffNbParams);
			}

			def_code += " : ";
			def_code += at_parent_class;
			def_code += "(";
			if (args->leaves->size())
			{
				for (unsigned i = 0; i < args->leaves->size() - 1; i++)
				{
					def_code += args->leaves->at(i)->compile();
					def_code += ", ";
				}
				def_code += args->leaves->at(args->leaves->size() - 1)->compile();
			}
			def_code += ")";
		}
		in_function = true;
		def_code += "\n{\n__init__();\n";
		def_code += super->second->compile();
		def_code += "}\n";
		in_function = false;
		return "";
	}
};

struct Super : public Symbol
{
	Token* nud(Token* super) 
	{
		super->arity = Token::Super;
		return super;
	}

	string compile(Token* super)
	{
		super->error(Error::Illegal);
		return "";
	}
};

struct Itself : public Symbol
{
	Token* nud(Token* super)
	{
		return super;
	}
};

struct String : public Symbol
{
	static String* Instance;

	Type type(Token*)
	{
		return Var;
	}

	String()
	{
		lbp = 0;
		id = "(string)";
	}

	Token* nud(Token* super)
	{
		return super;
	}

	string compile(Token* super)
	{
#ifdef WITH_ESCAPEMENT
		string code = "\"";
		code += super->value;
		code += "\"";
		return code;
#else
		return super->value;
#endif
	}
};

struct Integer : public Symbol
{
	static Integer* Instance;
	Integer()
	{
		lbp = 0;
		id = "(integer)";
	}

	Type type(Token*)
	{
		return Symbol::Integer;
	}

	Token* nud(Token* super)
	{
		return super;
	}

	string compile(Token* super)
	{
		return string(super->value) + "LL";
	}
};

struct Float : public Symbol
{
	static Float* Instance;
	Float()
	{
		lbp = 0;
		id = "(float)";
	}

	Type type(Token*)
	{
		return Double;
	}


	Token* nud(Token* super)
	{
		return super;
	}

	string compile(Token* super)
	{
		return super->value;
	}
};

struct Name : public Symbol
{
	Token* nud(Token* super)
	{
		return super;
	}

	Type type(Token* n)
	{
		return n->scope->getType(n->value);
	}

	string compile(Token* super)
	{
		Symbol* o = NULL;
		if (super->parent)
		{
			if (super->parent->second == super && (super->parent->id() == "." ||
				(super->parent->id() == "(" && super->parent->arity == Token::Ternary)))
			{
				switch(super->parent->first->arity)
				{
				case Token::This:
					o = class_scopes[at_class]->find(super->value);
					break;
				case Token::Super:
					o = class_scopes[at_parent_class]->find(super->value);
					break;
				case Token::Class:
					o = class_scopes[super->parent->first->value]->findStatic(super->value);
					break;
				default:
					o = virtual_scope->find(super->value);
				}
			}
			else if (super->parent->id() == "new")
			{
				o = constructor_scope->find(super->value);
			}
			else
			{
				o = super->scope->find(super->value);		
			}

			if (o)
			{
				return o->compile(super);
			}
			else
			{
				super->error(Error::NotDefined);
			}
		}

		return super->value;
	}
};

struct Curly_Bracket : public Symbol
{
	Type type(Token* n)
	{
		return Var;
	}

    Curly_Bracket()
    {
        has_std = true;
    }

	Token* nud(Token* super)
	{
		Token* a = new Token();
		if (token->id() != "}") {
			while (true) {
				if (token->arity != Token::Literal)
					token->error("String expected.");
				a->setFirst(token);
				advance();
				advance(":");
				a->setSecond(expression(9));
				super->push(a);
				if (token->id() != ",") {
					break;
				}
				advance(",");
			}
		}
		advance("}");
		super->arity = Token::Unary;
		return super;
	}

	Token* std(Token* super)
	{
        if (!in_function)
            super->error(Error::StatementNotInFunction);

		new_scope();
		super->setFirst(statements());
		advance("}");
		scope->pop();
		return super;
	}

	string compile(Token* super)
	{
		string code;
		if (super->arity == Token::Unary)
		{
			code = "(new Map())";
			if (super->leaves)
			for (int i = 0; i < super->leaves->size(); i++)
			{
				code += "->init(";
				code += super->leaves->at(i)->first->compile();
				code += ",";
				code += super->leaves->at(i)->second->compile();
				code += ")";
			}
		}
		else
		{
			code = "{\n";
			if (super->first->leaves)
			{
				for (unsigned i = 0; i < super->first->leaves->size(); i++)
				{
					code += super->first->leaves->at(i)->compile();
					code += ";\n";
				}
			
			}
			code += "}";
		}
		return code;
	}
};

Token* block()
{
	Token* t = token;
	advance("{");
	return t->std();
}

struct Var : public Symbol
{
    Var()
    {
        has_std = true;
    }

	Token* std(Token* super)
	{
        if (!in_class)
			super->error(Error::VarOutsideClass);

		Token* n,*t;
    	while (true) 
        {
			n = token;
			if (n->arity != Token::Name) {
				n->error(Error::ExpectedNewVarName);
			}
			scope->defineVar(n);

			if (!in_function)
			{
				class_scopes[at_class]->defineVar(n);
			}

			advance();
			if (token->id() == "=") 
            {
				t = token;
				advance("=");
				t->setFirst(n);
				t->setSecond(expression(9));
				t->arity = Token::Binary;
				t->first->setType(t->second);
                super->push(t);
			}
            else
            {
				n->setType(Symbol::Var);
                super->push(n);
            }
			if (token->id() != ",") {
				break;
			}
			advance(",");
		}

		advance(";");
		return super;
	}

	string compile(Token* super)
	{
		string code;
		Token* t;
		for (unsigned i = 0; i < super->leaves->size(); i++)
		{
			t = super->leaves->at(i);
			if (t->id() == "=")
			{
				Symbol::Type vtype = t->first->type();
				code += toCode(vtype);
				if (vtype == Ref)
				{
					code += "<";
					code += t->first->refType();
					code += ">";
				}
                code += " ";
				code += t->first->value;
				if (in_function)
				{
					code += " = ";
					code += t->second->compile();
				}
			}
			else 
			{
				code += toCode(t->type());
				code += " ";
				code += t->value;
			}
			if ( i < super->leaves->size() - 1)
				code += ";";
		}
		return code;
	}

	string compileDef(Token* super)
	{
		string code;
		Token* t;
		for (unsigned i = 0; i < super->leaves->size(); i++)
		{
			t = super->leaves->at(i);
			if (t->id() == "=")
			{
				code += t->first->value;
				code += " = ";
				code += t->second->compile();
				code += ";\n";
			}
			
		}
		
		return code;
	}
};

struct While : public  Symbol
{
    While()
    {
        has_std = true;
    }

	Token* std(Token* super)
	{
        if (!in_function)
			super->error(Error::StatementNotInFunction);
		in_loop = true;
		advance("(");
		super->setFirst(expression(9));
		advance(")");
		super->setSecond(block());
		super->arity = Token::Statement;
		in_loop = false;
		return super;
	}

	string compile(Token* super)
	{
		string code = "while";
		code += "(";
		code += super->first->compile();
		code += ")";
		code += "\n";
		code += super->second->compile();
		return code;
	}
};

struct Do : public  Symbol
{
    Do()
    {
        has_std = true;
    }

	Token* std(Token* super)
	{
        if (!in_function)
			super->error(Error::StatementNotInFunction);
		in_loop = true;
		super->setFirst(block());
		advance("while");
		advance("(");
		super->setSecond(expression(9));
		advance(")");
		advance(";");
		super->arity = Token::Statement;
		in_loop = false;
		return super;
	}

	string compile(Token* super)
	{
		string code = "do";
		code += super->first->compile();
		code += "while(";
		code += super->second->compile();
		code += ")";
		return code;
	}
};

struct For : public Symbol
{
	For()
	{
		has_std = true;
	}

	Token* std(Token* super)
	{
		if (!in_function)
            super->error(Error::StatementNotInFunction);
		in_loop = true;
		
		advance("(");
		Token* a = new Token();	
		bool nscope = false;
		if (token->id() != ";")
		{
			if (token->id() == "var")
			{
				new_scope();
				Token* v = token;
				v->scope = scope;
				nscope = true;
				advance("var");
				a->setFirst(v->std());
			}
			else 
			{
				a->setFirst(expression(0));
				advance(";");
			}
		} else advance(";");
		if (token->id() != ";")
			a->setSecond(expression(9));
		advance(";");
		if (token->id() != ")")
			a->setThird(expression(0));
		a->arity = Token::Ternary;
		super->setFirst(a);
		advance(")");
		super->setSecond(block());
		if (nscope)
			scope->pop();
		super->arity = Token::Statement;

		in_loop = false;
		return super;
	}

	string compile(Token* super)
	{
		string code = "for";
		code += "(";
		code += super->first->first->compile();
		code += ";";
		code += super->first->second->compile();
		code += ";";
		code += super->first->third->compile();
		code += ")";
		code += "\n";
		code += super->second->compile();
		return code;
	}
};

struct Foreach : public Symbol
{
	Foreach()
	{
		has_std = true;
	}

	Token* std(Token* super)
	{
		if (!in_function)
            super->error(Error::StatementNotInFunction);
		in_loop = true;
		
		advance("(");
		if (token->arity != Token::Name)
			token->error("must be a variable name.");
		scope->defineVar(token);
		super->setFirst(token);
		advance();
		advance("in");
		super->setSecond(expression(9));
		advance(")");
		super->setThird(block());
		super->arity = Token::Statement;

		in_loop = false;
		return super;
	}

	string compile(Token* super)
	{
		string code = "Array& __a__ = (Array&)*(";
		code += super->second->compile();
		code += ".o); ";
		code += "for (Array::iterator __it__ = __a__.begin(); __it__ < __a__.end(); ++__it__) { var& ";
		code += super->first->value;
		code += " = *__it__;\n";
		code += super->third->compile();
		code += "}";
		return code;
	}
};


struct If : public Symbol
{
    If()
    {
        has_std = true;
    }

	Token* std(Token* super)
	{
        if (!in_function)
            super->error(Error::StatementNotInFunction);
		advance("(");
		super->setFirst(expression(9));
		advance(")");
		super->setSecond(block());
		if (token->id() == "else") {
			advance("else");
			super->setThird(token->id() == "if" ? statement() : block());
		} else {
			super->third = NULL;
		}
		super->arity = Token::Statement;
		return super;
	}

	string compile(Token* super)
	{
		string code = "if";
		code += "(";
		code += super->first->compile();
		code += ")";
		code += "\n";
		code += super->second->compile();
		if (super->third)
		{
			code += "\nelse\n";
			code += super->third->compile();
		}
		return code;
	}
};

struct Break : public Symbol
{
    Break()
    {
        has_std = true;
    }

	Token* std(Token* super)
	{
        if (!in_loop)
            super->error(Error::Illegal);

		advance(";");
		if (token->id() != "}") {
			token->error(Error::UnreachableStatement);
		}
		super->arity = Token::Statement;
		return super;
	}
};

struct Return : public Symbol
{
    Return()
    {
        has_std = true;
    }

	Token* std(Token* super)
	{
        if (!in_function)
            super->error(Error::StatementNotInFunction);

		if (token->id() != ";") {
			super->setFirst(expression(9));
		}
		advance(";");
		if (token->id() != "}") {
			token->error(Error::UnreachableStatement);
		}
		super->arity = Token::Statement;
		return super;
	}

	string compile(Token* super)
	{
		string code = "return ";
		code += super->first->compile();
		return code;
	}
};


struct Function : public Symbol
{
	string attribute;

    Function()
    {
        has_std = true;
    }

	Token* std(Token* super)
	{
        if (!in_class)
            super->error(Error::FuncOutsideClass);

        if (in_function)
            super->error(Error::FuncInsideFunc);

		
		Token* name = token;
		if (name->arity != Token::Name) 
		{
			name->error(Error::UnexpectedFuncName);
		}
		super->value = name->value;
		advance();
	
        new_scope();
		advance("(");        
		int args = 0;
        Token* a = new Token();
		if (token->id() != ")") 
		{
			while (true) 
			{
				if (token->arity != Token::Name) {
					token->error(Error::ExpectedParamName);
				}
				scope->defineVar(token);
				a->push(token);
				args++;
				advance();
				if (token->id() != ",") {
					break;
				}
				advance(",");
			}
		}

		scope->parent->defineFunction(name, args);
		if (attribute == "public:")
		{
			virtual_scope->defineFunction(name, args, true);
			class_scopes[at_class]->defineFunction(name, args);
		}
		else if (attribute == "private:")
		{
			class_scopes[at_class]->defineFunction(name, args);
		}
		else //static
		{
			class_scopes[at_class]->defineStaticFunction(name, args);
		}
		super->setFirst(a);
		advance(")");
		if (token->id() == "=")
		{
			advance("=");
			super->setSecond(expression(9));
			scope->pop();
			advance(";");
		}
		else
		{
			advance("{");
	        in_function = true;
			super->setSecond(statements());
	        scope->pop();
			advance("}");
			in_function = false;
		}
		super->arity = Token::Function;
        
		return super;
	}

	string compile(Token* super)
	{
		string code = attribute;
		code +=	" var ";
		code += super->value;
		code += "(";
		if (super->first->leaves)
		for (unsigned i = 0; i < super->first->leaves->size(); i++)
		{
			code += "var ";
			code += super->first->leaves->at(i)->compile();
			if (i < super->first->leaves->size() - 1)
				code += ", ";
		}
		code += ")";
		return code;
	}

	string compileDef(Token* super)
	{
		def_code += "var ";
		def_code += at_class;
		def_code += "::";
		def_code += super->value;
		def_code += "(";
		if (super->first->leaves)
		for (unsigned i = 0; i < super->first->leaves->size(); i++)
		{
			def_code += "var ";
			def_code += super->first->leaves->at(i)->compile();
			if (i < super->first->leaves->size() - 1)
				def_code += ", ";
		}
		def_code += ")\n{\n";
		if (super->second->leaves)
		{
			in_function = true;
			def_code += super->second->compile();
			def_code += "\nreturn var::null;\n}\n";
			in_function = false;
		}
		else
		{
			def_code += "return ";
			def_code += super->second->compile();
			def_code += ";\n}\n";
		}
		return "";
	}
};


struct Public : public Function
{
    Public()
    {
		attribute = "public:";
    }
};

struct Private : public Function
{
    Private()
    {
		attribute = "private:";
    }
};

struct Static : public Function
{
    Static()
    {
		attribute = "public: static";
    }
};

struct Class : public Symbol
{
    Class()
    {
        has_std = true;
    }

    Token* std(Token* super)
    {
        Token* s;
        if (token->arity != Token::Name)
        {
			token->error(Error::UnexpectedClassName);
        }
		at_class = token->value;
		super->value = token->value;
		if (class_scopes[at_class])
			super->error(Error::AlreadyDefined);
		else
			class_scopes[at_class] = new Scope();

		constructor_scope->defineFunction(at_class, 0);

        advance();
		if (token->id() == ":")
		{
			advance(":");
			if (token->arity != Token::Name)
	        {
	            token->error(Error::UnexpectedClassName);
	        }
			super->setFirst(token);			
			advance();
		}

        new_scope();
        advance("{");
        in_class = true;
        while (true)
        {
            if (token->id() == "}")
                break;
			string& id = token->id();
            if (id != "var" && 
				id != "private" && 
				id != "public" && 
				id != "this" &&
				id != "static")
				token->error(Error::UnexpectedStatement);    
            s = statement();
            if (s) {
                super->push(s);
            }
        }
        scope->pop();
        in_class = false;
		super->arity = Token::Class;
        advance("}");

        return super;
    }

	string compile(Token* super)
	{
		string code = "class ";
		code +=	super->value;
		if (super->first)
		{
			code += " : public ";
			if (not_declared[super->first->value])
			{
				// declare super class first
				code = not_declared[super->first->value]->compile() + code;
			}
	
			code += super->first->value;
			at_parent_class = super->first->value;
		}
		else
		{
			code += " : public Object";
		}

		at_class = super->value;
		string init_code;
		code += "\n{\ninline void __init__();\n";

		bool defaultFound = false;
		if (super->leaves)
		for (unsigned i = 0; i < super->leaves->size(); i++)
		{
			Token* t = super->leaves->at(i);
			code += t->compile();
			code += ";\n";

			init_code += t->compileDef();

			if (t->id() == "this" && t->first->leaves->size() == 0)
			{
				defaultFound = true;
			}
		}

		if (!defaultFound)
		{
			code += at_class;
			code += "() { __init__(); }\n";
		}
		code += "};\n\n";

		def_code += "void ";
		def_code += super->value;
		def_code += "::__init__()\n{\n";
		def_code += init_code;
		def_code += "}\n";

		// mark class as declared
		not_declared[at_class] = NULL;
		return code;
	}
};


#endif
